package com.uziot.activiti6.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.uziot.activiti6.common.dto.ModelDTO;
import com.uziot.activiti6.service.ModelService;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;

/**
 * @author shidt
 * @version V1.0
 * @className ModelServiceImpl
 * @date 2021-03-23 22:56:19
 * @description
 */

@Slf4j
@Service
public class ModelServiceImpl implements ModelService {
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public String create(ModelDTO modelDto) {

        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put("id", "canvas");
        editorNode.put("resourceId", "canvas");
        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
        editorNode.put("stencilset", stencilSetNode);
        Model model = repositoryService.newModel();

        ObjectNode modelObjectNode = objectMapper.createObjectNode();
        modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, modelDto.getModelName());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, modelDto.getDescription());
        model.setMetaInfo(modelObjectNode.toString());
        model.setName(modelDto.getModelName());
        model.setKey(modelDto.getModelKey());

        log.debug("editorNode.toString() = {}", editorNode.toString());

        // 保存模型
        repositoryService.saveModel(model);
        repositoryService.addModelEditorSource(model.getId(), editorNode.toString().getBytes(StandardCharsets.UTF_8));
        return model.getId();
    }

    /**
     * 上传bpmn文件作为待编辑对象
     *
     * @param modelFile 文件
     * @return 文件
     */
    @Override
    public HashMap<String, Object> uploadModel(MultipartFile modelFile) {
        HashMap<String, Object> result = new HashMap<>(6);
        String fileName = modelFile.getOriginalFilename();

        if (StringUtils.isEmpty(fileName)) {
            throw new ActivitiException("没有读取到正确的文件名信息！");
        }
        if (!(fileName.endsWith(".bpmn20.xml") || fileName.endsWith(".bpmn"))) {
            throw new ActivitiException("后缀名无效，可能不是工作流定义文件！");
        }

        // 执行文件流的校验和保存流程
        InputStreamReader in = null;
        try {
            in = new InputStreamReader(new ByteArrayInputStream(modelFile.getBytes()), StandardCharsets.UTF_8);
            XMLInputFactory xif = XMLInputFactory.newInstance();
            XMLStreamReader xtr = xif.createXMLStreamReader(in);
            BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
            if (bpmnModel.getMainProcess() == null || bpmnModel.getMainProcess().getId() == null) {
                throw new ActivitiException("数据模型无效，必须有一条主流程");
            }
            if (bpmnModel.getLocationMap().isEmpty()) {
                throw new ActivitiException("locationMap为空");
            }
            String processName;
            if (StringUtils.isNotEmpty(bpmnModel.getMainProcess().getName())) {
                processName = bpmnModel.getMainProcess().getName();
            } else {
                processName = bpmnModel.getMainProcess().getId();
            }
            Model modelData = repositoryService.newModel();
            ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
            modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processName);
            modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
            modelData.setMetaInfo(modelObjectNode.toString());
            modelData.setName(processName);

            // 保存模型文件
            repositoryService.saveModel(modelData);

            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
            ObjectNode editorNode = jsonConverter.convertToJson(bpmnModel);

            // 打印日志内容
            byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(bpmnModel);
            if (log.isInfoEnabled()) {
                log.info("上传的bpmn文件内容为：{}", new String(bpmnBytes, StandardCharsets.UTF_8));
            }
            byte[] bytes = editorNode.toString().getBytes(StandardCharsets.UTF_8);
            // 添加编辑内容
            repositoryService.addModelEditorSource(modelData.getId(), bytes);
            result.put("modelId", modelData.getId());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new ActivitiException("上传工作流文档发生异常！");
        } finally {
            IOUtils.closeQuietly(in);
        }
        return result;
    }
}
